/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.geftu.util;

import br.com.arthutils.beans.TableField;
import br.com.arthutils.misc.reflect.FieldsManager;
import java.util.List;
import javax.swing.table.AbstractTableModel;
/**
 *
 * @author jorgenei
 */
public class TableModelback<T> extends AbstractTableModel {
 
    private FieldsManager fieldsManager = null;

    private List<T> data = null;
    private List<TableField> columns = null;

    /**
     * Cria um novo TableModel
     * 
     * @param data a lista de dados a ser mostrados
     * @param modelOf a classe da qual se faz a referencia o modelo
     * @param visibleFields os campos que serao visiveis na tabela
     */
    public TableModelback(List<T> data, Class<T> modelOf, String... visibleFields){

        this.fieldsManager = new FieldsManager(modelOf, visibleFields);

        this.data = data;
        this.columns = fieldsManager.resolveFields();
    }

    /**
     * Captura um objeto do model
     *
     * @param row a linha que contem o objeto desejado
     * 
     * @return o objeto
     */
    public T getObjectOf(int row) {
        return data.get(row);
    }

    /**
     * Remove um objeto do model
     *
     * @see AbstractTableModel#fireTableDataChanged()
     *
     * @param t o objeto
     */
    public void removeObject(T t) {
        this.data.remove(t);
        fireTableDataChanged();
    }

    /**
     * Remove uma linha do model e notifica sobre a alteracao
     *
     * @see AbstractTableModel#fireTableDataChanged()
     *
     * @param row a linha
     */
    public void removeRow(int row) {
        this.data.remove(row);
        fireTableDataChanged();
    }

    /**
     * Insere uma linha no fim da tabela
     *
     * @param t o elemento
     */
    public void insertRow(T t) {
        this.data.add(t);
        fireTableDataChanged();
    }

    /**
     * Insere uma linha antes do local indicado
     *
     * @param t o objeto a ser inserido
     * @param row a linha de referencia
     */
    public void insertRowBefore(T t, int row) {

        if ((row - 2) < 0 || row > data.size()) {
            row = 0;
        }

        this.data.add(row, t);
        fireTableDataChanged();
    }

    /**
     * Insere uma linha apos do local indicado
     *
     * @param t o objeto a ser inserido
     * @param row a linha de referencia
     */
    public void insertRowAfter(T t, int row) {

        if (row > data.size() || row < 0) {
            row = data.size();
        }

        this.data.add(row, t);
        fireTableDataChanged();
    }

    /**
     * Captura a fonte de dados do model
     * 
     * @return uma lista dos dados
     */
    public List<T> getData() {
        return data;
    }

    /**
     * Define um novo DataSource para o model
     *
     * @param data a nova fonte de dados
     *
     * @see AbstractTableModel#fireTableDataChanged()
     */
    public void setData(List<T> data) {
        
        if (this.data.isEmpty()) {
            this.data = data;
        } else {
            this.data.addAll(data);
        }

        fireTableDataChanged();
    }

    /**
     * Limpa a fonte de dados do Model, e notifica a listener que o modelo mudou
     *
     * @see AbstractTableModel#fireTableDataChanged()
     */
    public void clearData() {
        this.data.clear();
        fireTableDataChanged();
    }

    /**
     * Retorna se o modelo esta ou nao vazio
     * 
     * @return true se vazio, false se nao
     */
    public boolean isEmpty() {
        return this.data.isEmpty();
    }

    /**
     * Captura a classe dos elemenstos de uma coluna
     *
     * @param columnIndex a coluna
     *
     * @return a classe dos elementos
     */
    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return this.columns.get(columnIndex).getType();
    }

    /**
     * Captura os nomes das colunas
     *
     * @param column a coluna da qual se quer o nome
     *
     * @return o nome da coluna
     */
    @Override
    public String getColumnName(int column) {
        return columns.get(column).getName();
    }

    /**
     * Captura a quantidade de linhas da tabela
     *
     * @return a quantidade de linhas
     */
    @Override
    public int getRowCount() {
        return this.data.size();
    }

    /**
     * Captura a quantidade de colunas da tabela
     *
     * @return a quantidade de colunas
     */
    @Override
    public int getColumnCount() {
        return this.columns.size();
    }

    /**
     * Utilizado para transpor os valores nas celulas da tabela
     *
     * @param rowIndex indice da linha
     * @param columnIndex indice da coluna
     *
     * @return o valor da celula
     */
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {

        Object toShow = data.get(rowIndex);

        try {

            if (columns.get(columnIndex).isIsBlank()) {
                return null;
            } else {
                return columns.get(columnIndex).getValue(toShow);
            }
        } catch (Exception ex) {
            System.err.println(
                    "Erro ao capturar valores para a tabela no campo: "
                    + columns.get(columnIndex).getName());
            return null;
        }
    }

    /**
     * Metodo utilizado para setar o valor em uma celula quando ela for editavel
     *
     * @param aValue o valor
     * @param rowIndex o indice da linha
     * @param columnIndex o indice da coluna
     */
    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {

        Object toSet = data.get(rowIndex);

        try {

            if (columns.get(columnIndex).isIsBlank()) {
                columns.get(columnIndex).notBlank();
            } 

            columns.get(columnIndex).setValue(toSet, aValue);
        } catch (Exception ex) {
            System.err.println("Erro ao definir o valor na celula.\n"
                    + ex.getMessage());
            return;
        }
    }
}
